package simmar.xcs;

import java.util.Random;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import simmar.utilities.Logger;

public class XCSEdit implements XCSConfig
{    
    protected static Random randomNumber = new Random(System.currentTimeMillis());
    
    public static void insertClassifier(
            XCSClassifier classifier, 
            Map<String,XCSClassifier> population
            )
    {
//        System.out.println("---------------------------------------------------------------------------------------------------------");
//        Logger.log("**** XCS INSERT CLASSIFIER ****\n");
//        System.out.println("---------------------------------------------------------------------------------------------------------");
        
        Iterator<Entry<String,XCSClassifier>> it = population.entrySet().iterator();

        while (it.hasNext())
        {
            XCSClassifier nextClassifier = it.next().getValue();
            
            if (nextClassifier.getRule().isEqualTo(classifier.getRule()))
            {
                nextClassifier.incrementNumerosity();
                
                if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.NORMAL) > 0)
                    Logger.logLine("Incrementing numerosity for classifier: " + nextClassifier.getRule().toString());

                return;
            }
        }
        
        String key = classifier.getRule().toString();
        population.put(key, classifier);
        
        if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.NORMAL) > 0)
            Logger.logLine("Inserted classifier " + key + " in population");
    }

    public static void deleteClassifier(Map<String,XCSClassifier> population)
    {
//        System.out.println("---------------------------------------------------------------------------------------------------------");
//        Logger.log("**** XCS DELETE CLASSIFIER ****\n");
//        System.out.println("---------------------------------------------------------------------------------------------------------");
        
        int populationSize = XCSEdit.getPopulationSize(population);
        
        if (XCSEdit.getPopulationSize(population) <= CLASSIFIER_POPULATION_SIZE)
            return;
        
        float fitnessSum = 0.0f;
        
        Iterator<Entry<String,XCSClassifier>> it1 = population.entrySet().iterator();
        
        while (it1.hasNext())
            fitnessSum += it1.next().getValue().getFitness();
        
        float averageFitness = fitnessSum / populationSize;
        
        // Find roulette wheel threshold
        int voteSum = 0;
        
        Iterator<Entry<String,XCSClassifier>> it2 = population.entrySet().iterator();
        
        while (it2.hasNext())
            voteSum += deletionVote(it2.next().getValue(), averageFitness);
        
        int threshold = Math.round((randomNumber.nextFloat() * voteSum));
        
        voteSum = 0;
        
        // Decrement numerosity for classifier or remove it
        Iterator<Entry<String,XCSClassifier>> it3 = population.entrySet().iterator();
        
        while (it3.hasNext())
        {
            XCSClassifier nextClassifier = it3.next().getValue();
            voteSum += deletionVote(nextClassifier, averageFitness);
            
            if (voteSum > threshold)
            {                
                if (nextClassifier.getNumerosity() > 1)
                {
                    nextClassifier.decrementNumerosity();
                    
                    if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.NORMAL) > 0)
                        Logger.logLine("Decrementing numerosity for classifier: " + nextClassifier.getRule().toString());                    
                }
                else
                {
                    String key = nextClassifier.getRule().toString();
                    if (population.containsKey(key))
                    {
                        population.remove(key);
                        
                        if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.NORMAL) > 0)
                            Logger.logLine("Removed classifier " + nextClassifier.getRule().toString() + " from population");
                    }                        
                    else
                    {
                        Logger.logLine("ERROR: Object was not present in the dictionary. Exit program.");
                        System.exit(0);
                    }
                }

                return;
            }
        }
    }

    protected static int deletionVote(XCSClassifier classifier, float averageFitness)
    {
//        System.out.println("---------------------------------------------------------------------------------------------------------");
//        Logger.log("**** XCS DELETION VOTE ****\n");
//        System.out.println("---------------------------------------------------------------------------------------------------------");
        
        int vote = classifier.getActionSetSize() * classifier.getNumerosity();

        if (classifier.getExperience() > DELETION_THRESHOLD 
                && ((classifier.getFitness() / classifier.getNumerosity()) < (FITNESS_FRACTION_FOR_DELETION * averageFitness)))
            vote *= averageFitness / (classifier.getFitness() / classifier.getNumerosity());

        return vote;
    }
    
    public static int getPopulationSize(Map<String,XCSClassifier> population)
    {
        int count = 0;
        Iterator<Entry<String,XCSClassifier>> it = population.entrySet().iterator();

        while (it.hasNext())
            count += it.next().getValue().getNumerosity();
        
        return count;
    }
}
